package whf.framework.validate;

import java.beans.PropertyDescriptor;
import java.util.Collection;
import java.util.Date;
import java.util.Set;

import whf.framework.entity.Entity;
import whf.framework.exception.ServiceNotFoundException;
import whf.framework.i18n.ApplicationResource;
import whf.framework.meta.entity.IReadonlyProperty;
import whf.framework.util.BeanFactory;
import whf.framework.util.BeanUtils;
import whf.framework.util.DateUtils;
import whf.framework.util.StringUtils;
import whf.framework.util.TypeUtils;

/**
 * @author wanghaifeng
 *
 */
public class ValidateServiceImp implements ValidateService{
	
	public static ValidateService getValidateService() throws ServiceNotFoundException{
		return (ValidateService)BeanFactory.getService("ValidateService");
	}
	
	private double getNumericValue(Entity bo, String propertyName, double defaultValue){
		if(propertyName!= null && propertyName.length() > 1 ){
			try{
				double d = Double.parseDouble(BeanUtils.getProperty(bo, propertyName).toString());
				return d;
			}catch(Exception e){
				//ignore
			}
		}
		return defaultValue;
	}
	
	private Date getDateValue(Entity bo, String propertyName){
		try{
			if(propertyName!= null && propertyName.length() > 1 ){
				return (Date)BeanUtils.getProperty(bo, propertyName);
			}
		}catch(Exception inore){
			//
		}
		return null;
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.validate.IValidate#validate(whf.framework.entity.IBaseBO, whf.framework.meta.Meta, java.lang.String)
	 */
	public void validate(Entity form, Set<IReadonlyProperty> properties, String objectId) throws ValidateException {
		PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(form.getClass());
		ValidateErrors errors = new ValidateErrors();
		for(PropertyDescriptor descriptor: descriptors){
			String propertyName = descriptor.getName();
			Class propertyType = descriptor.getPropertyType();
			IReadonlyProperty property = null;
			for(IReadonlyProperty prop: properties){
				if(StringUtils.equals(propertyName, prop.getName())){
					property = prop;
					break;
				}
			}
			if(property == null) continue;
			try{
				Object value = BeanUtils.getProperty(form, propertyName);
				//required validate
				if(property.isRequired()){
					if(value == null || 
							StringUtils.isEmpty(value.toString()) || 
							(value instanceof Collection) && ((Collection)value).isEmpty()){
						errors.put(objectId+propertyName, ApplicationResource.get(PROPERTY_REQUIRED, property.getDisplayTitle()));
						continue;
					}
				}
				if(value == null) continue;
				//range validate
				if(propertyType == int.class || propertyType == long.class 
						|| propertyType == float.class || propertyType == double.class 
						|| propertyType == Integer.class || propertyType == Long.class 
						|| propertyType == Float.class || propertyType == Double.class){
					double numericValue = Double.parseDouble(value.toString());
					
					double maxValue = 0;
					boolean maxRequired = false;
					if(property.getMaxValue() != null && property.getMaxValue().startsWith("$") && property.getMaxValue().length() > 1){ //one property value less than another property value
						maxValue = this.getNumericValue(form, property.getMaxValue().substring(1), 0);
						maxRequired = true;
					}else if(StringUtils.isNumeric(property.getMaxValue())){
						maxValue = Double.parseDouble(property.getMaxValue());
						maxRequired = true;
					}
					double minValue = 0;
					boolean minRequired = false;
					if(property.getMinValue() != null && property.getMinValue().startsWith("$") && property.getMinValue().length() > 1){ //one property value more than another property value
						minValue = this.getNumericValue(form, property.getMinValue().substring(1), 0);
						minRequired = true;
					}else if(StringUtils.isNumeric(property.getMinValue())){
						minValue = Double.parseDouble(property.getMinValue());
						minRequired = true;
					}
					if(maxRequired && minRequired && 
							(numericValue >maxValue || numericValue < minValue)){
						errors.put(objectId+propertyName, ApplicationResource.get(PROPERTY_RANGE_BETWEEN, new String[]{property.getDisplayTitle(),property.getMinValue(), property.getMaxValue()}));
					} else if(maxRequired && numericValue > maxValue){
						errors.put(objectId+propertyName, ApplicationResource.get(PROPERTY_RANGE_LESS_THAN, new String[]{property.getDisplayTitle(),property.getMaxValue()}));
					} else if(minRequired && numericValue < minValue){
						errors.put(objectId+propertyName, ApplicationResource.get(PROPERTY_RANGE_GREAT_THAN, new String[]{property.getDisplayTitle(),property.getMinValue()}));
					}
				} else if(TypeUtils.isExtendsFrom(propertyType, Date.class)){
					Date dateValue = (Date)value;
					
					Date minValue = null;
					if(property.getMinValue() != null && property.getMinValue().startsWith("$") && property.getMinValue().length() > 1){
						minValue = this.getDateValue(form, property.getMinValue().substring(1));
					} else {
						minValue = DateUtils.parseDate(property.getMinValue());
					}
					Date maxValue = null;
					if(property.getMaxValue() != null && property.getMaxValue().startsWith("$") && property.getMaxValue().length() > 1){
						maxValue = this.getDateValue(form, property.getMaxValue().substring(1));
					} else {
						maxValue = DateUtils.parseDate(property.getMaxValue());
					}
					if(minValue != null && maxValue != null && 
							(dateValue.compareTo(minValue) < 0 || dateValue.compareTo(maxValue) > 0) ){
						errors.put(objectId+propertyName, ApplicationResource.get(PROPERTY_RANGE_BETWEEN, new String[]{property.getDisplayTitle(),property.getMinValue(), property.getMaxValue()}));
					} else if(maxValue != null && dateValue.compareTo(maxValue) < 0 ){
						errors.put(objectId+propertyName, ApplicationResource.get(PROPERTY_RANGE_LESS_THAN, new String[]{property.getDisplayTitle(),property.getMaxValue()}));
					} else if(minValue != null && dateValue.compareTo(minValue) > 0){
						errors.put(objectId+propertyName, ApplicationResource.get(PROPERTY_RANGE_GREAT_THAN, new String[]{property.getDisplayTitle(),property.getMinValue()}));
					}
				} 
				if(propertyType == String.class && value != null){
					String strValue = (String)value;
					if(strValue.length() > property.getMaxLength() && property.getMaxLength() > 0){
						errors.put(objectId+propertyName, ApplicationResource.get(PROPERTY_LENGTH_LESS_THAN, new String[]{property.getDisplayTitle(),String.valueOf(property.getMaxLength())}));
					}
					//正则表达式验证
					if(!StringUtils.isEmpty(strValue) && ! StringUtils.isEmpty(property.getRegexPattern())){
						if(!StringUtils.match(property.getRegexPattern(), strValue)){
							errors.put(objectId+propertyName, ApplicationResource.get(PROPERTY_LENGTH_LESS_THAN, new String[]{property.getDisplayTitle(), property.getRegexPattern(), property.getRegexPatternSample()}));
						}
					}
				}
			}catch(Exception ex){
				errors.put(objectId+propertyName, ex.getMessage());
			}
		}
		if(!errors.isEmpty()){
			throw new ValidateException(errors);
		}
	}

	public static void main(String[] args) {
		Integer i1 = new Integer(1);
		Integer i2 = new Integer(2);
		System.out.println(i1.compareTo(i2));
	}
}
